package com.wlyuan.index.domain.source;

import com.wlyuan.core.domain.valueobj.*;
import com.wlyuan.core.named.*;
import com.wlyuan.index.domain.source.valueobj.Address;
import com.wlyuan.index.domain.source.valueobj.Character;
import com.wlyuan.index.domain.source.valueobj.CustomerType;
import com.wlyuan.index.domain.source.valueobj.*;
import com.wlyuan.index.domain.source.valueobj.Statistics;
import com.wlyuan.tools.utils.CallUtils;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@AllArgsConstructor
@Builder
@Getter
@ToString
public final class Waybill {

    @Getter
    @AllArgsConstructor
    @Builder
    @ToString
    public static final class Booking {
        private Integer status;
        private LocalDateTime arrivalTime;
    }

    @Getter
    @AllArgsConstructor
    @Builder
    @ToString
    public static final class Combine {
        @ApiModelProperty("合单ID")
        private CombineId combineId;
        @ApiModelProperty("合同号")
        private ContractNo contractNo;
        @ApiModelProperty("合单编号")
        private CombineNo combineNo;
    }

    @AllArgsConstructor
    @Builder
    @Getter
    @ToString
    public static final class Vehicle {
        @ApiModelProperty(value = "车型")
        private VehicleModel model;

        @ApiModelProperty(value = "车长")
        private VehicleLength length;

        @ApiModelProperty(value = "数量")
        private Integer quantity;
    }


    @AllArgsConstructor
    @Builder
    @Getter
    @ToString
    public static final class Device {
        @ApiModelProperty("设备编号")
        private DeviceCode deviceCode;

        @ApiModelProperty("湿度")
        private Boolean humidity;

        @ApiModelProperty("温度")
        private Boolean temperature;
    }

    @AllArgsConstructor
    @Builder
    @Getter
    @ToString
    public static class Relation {
        private Character character;
        private WaybillStatus status;
        private Boolean stopQuote;
        private String customerName;
        private List<Highlight> highlights;
        private TenantId tenantId;

        public void addHighlight(Highlight highlight, Integer status) {
            if (!Highlight.marked(status)) {
                return;
            }
            synchronized (this) {
                if (null == this.highlights) {
                    this.highlights = new ArrayList<>();
                }
            }
            this.highlights.add(highlight);
        }

        protected Relation(Relation relation) {
            this.character = relation.character;
            this.status = relation.status;
            this.stopQuote = relation.stopQuote;
            this.customerName = relation.customerName;
            this.highlights = relation.highlights;
            this.tenantId = relation.tenantId;
        }
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    @Builder
    @Getter
    @ToString
    public static final class Receipt {
        private ReceiptType type;
        private Integer quantity;
        private Integer exceptionQuantity;
        private Integer lackQuantity;
        private Integer loseQuantity;
        private Integer damageQuantity;
        private Integer otherQuantity;
        @ApiModelProperty("复核")
        private Boolean reviewed;
        @ApiModelProperty("补单")
        private Boolean recovered;
        private LocalDateTime receiptAt;
        private LocalDateTime receiptSignAt;
        private UserId receiptBy;
        private TenantId tenantId;
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    @Builder
    @Getter
    @ToString
    public static final class Sign {
        private SignType type;
        private Integer signatory;
        private Integer quantity;
        private Integer exceptionQuantity;
        private Integer lackQuantity;
        private Integer loseQuantity;
        private Integer damageQuantity;
        private Integer otherQuantity;
        private LocalDateTime signAt;
        private UserId signBy;
        private TenantId tenantId;
    }

    @AllArgsConstructor(access = AccessLevel.PRIVATE)
    @Builder
    @Getter
    @ToString
    public static final class Close {
        @ApiModelProperty("关单原因")
        private String reason;

        @ApiModelProperty("关单类型")
        private Integer type;

        @ApiModelProperty("关单状态 10撤回中, 20待审核, 30拒绝中,40拒绝,50关单中，60同意")
        private Integer status;

        @ApiModelProperty("租户ID: 发起租户")
        private Long tenantId;
    }

    @AllArgsConstructor
    @Getter
    @ToString
    public static final class Driver {
        private DriverId driverId;
        private String name;
        private String mobile;
        private VehicleId vehicleId;
        private String plateNo;
        private TenantId tenantId;
    }

    @AllArgsConstructor
    @Builder
    @Getter
    @ToString
    public static final class Arguments {
        private PackageMode packageMode;
        private BillingMode billingMode;
        private BillingUnit billingUnit;
        private CargoType cargoType;
        private Boolean pick;
        private Boolean deliver;
        private Boolean load;
        private Boolean unload;
        private Boolean insure;
    }


    @ApiModelProperty("运单ID")
    private WaybillId waybillId;

    @ApiModelProperty("上级运单ID")
    private WaybillId superiorId;

    @ApiModelProperty("订单ID")
    private OrderId orderId;

    @ApiModelProperty("合同号")
    private ContractNo contractNo;

    @ApiModelProperty("跟踪单号")
    private String trackNo;

    @ApiModelProperty("跟踪类型")
    private TrackType trackType;

    @ApiModelProperty("发货时间")
    private LocalDateTime fromTime;

    @ApiModelProperty("发货地址")
    private Address fromAddress;

    @ApiModelProperty("发货方信息")
    private Customer customer;

    @ApiModelProperty("收货方信息")
    private Consignee consignee;

    @ApiModelProperty("收货时间")
    private LocalDateTime toTime;

    @ApiModelProperty("收货地址")
    private Address toAddress;

    @ApiModelProperty("承运方信息")
    private Carrier carrier;

    @ApiModelProperty("司机信息")
    private Driver driver;

    @ApiModelProperty("运输模式")
    private TransportMode transportMode;

    @ApiModelProperty("运输距离")
    private DistanceType distanceType;

    @ApiModelProperty("运输参数")
    private Arguments arguments;

    @ApiModelProperty("运输车辆")
    private Vehicle vehicle;

    @ApiModelProperty("备注")
    private String remarks;

    @ApiModelProperty("设备信息")
    private Device device;

    @ApiModelProperty("报价信息")
    private Quote quote;

    @ApiModelProperty("结算信息")
    private Balance balance;

    @ApiModelProperty("关单信息")
    private Close close;

    @ApiModelProperty("物品统计")
    private Statistics statistics;

    @ApiModelProperty("进场时间")
    private LocalDateTime arriveAt;

    @ApiModelProperty("创建时间")
    private LocalDateTime createAt;

    @ApiModelProperty("创建人")
    private UserId createBy;

    @ApiModelProperty("租户ID")
    private TenantId tenantId;

    @ApiModelProperty("运输关系")
    public List<Relation> relations;

    @ApiModelProperty("提货预约")
    private Booking booking;

    @ApiModelProperty("是否可以提货预约")
    private Boolean bookingEnabled;

    @ApiModelProperty("是否被合单")
    private Boolean combined;

    @ApiModelProperty("合单信息")
    private Combine combine;

    @ApiModelProperty("是否被拆单")
    private Boolean divided;

    @ApiModelProperty("是否从单")
    private Boolean affiliated;

    @ApiModelProperty("是否原始运单")
    private Boolean original;

    @ApiModelProperty("是否有费用")
    private Boolean hasFees;

    @ApiModelProperty("是否过关账期")
    private Boolean endBill;

    @ApiModelProperty("报价单信息")
    public final List<Quotation> quotations = new ArrayList<>();

    @ApiModelProperty("付款信息")
    public final List<Balance> balances = new ArrayList<>();

    @ApiModelProperty("账单信息")
    public final List<Bill> bills = new ArrayList<>();

    @ApiModelProperty("签收信息")
    private final List<Sign> signs = new ArrayList<>();

    @ApiModelProperty("回单信息")
    private final List<Receipt> receipts = new ArrayList<>();

    public void addCombine(Combine combine) {
        this.combine = combine;
        this.combined = true;
    }

    public void addDevice(Device device) {
        this.device = device;
    }

    public void addDriver(Driver driver) {
        this.driver = driver;
    }

    public void addQuote(Quote quote) {
        this.quote = quote;
    }

    public void addSign(Sign sign) {
        this.signs.add(sign);
    }

    public void addReceipt(Receipt receipt) {
        this.receipts.add(receipt);
    }

    public void addBalance(Balance balance) {
        this.balances.add(balance);
    }

    public void addBalances(List<Balance> balances) {
        if (CollectionUtils.isEmpty(balances)) {
            return;
        }
        balances.forEach(this::addBalance);
    }

    public void addBill(Bill bill) {
        this.bills.add(bill);
    }

    public void addBills(List<Bill> bills) {
        if (CollectionUtils.isEmpty(bills)) {
            return;
        }
        bills.forEach(this::addBill);
    }

    public void addQuotation(Quotation quotation) {
        this.quotations.add(quotation);
    }

    public void addQuotations(List<Quotation> quotations) {
        if (CollectionUtils.isEmpty(quotations)) {
            return;
        }
        quotations.forEach(this::addQuotation);
    }

    public Set<String> getKeywords() {
        var keywords = new Keywords();
        keywords.add(this.contractNo);
        keywords.add(this.customer);
        keywords.add(this.fromAddress);
        keywords.add(this.toAddress);
        keywords.add(this.consignee);
        return StreamSupport.stream(keywords.spliterator(), false)
                .collect(Collectors.toSet());
    }

    private List<Quotation> getQuotations(TenantId tenantId) {
        return CallUtils.filter(Quotation::getTenantId, tenantId, this.quotations);
    }

    private List<Bill> getBills(TenantId tenantId) {
        return CallUtils.filter(Bill::getTenantId, tenantId, this.bills);
    }

    private List<Balance> getBalances(TenantId tenantId) {
        return CallUtils.filter(Balance::getTenantId, tenantId, this.balances);
    }

    private List<Receipt> getReceipts(TenantId tenantId) {
        return CallUtils.filter(Receipt::getTenantId, tenantId, this.receipts);
    }

    private List<Sign> getSigns(TenantId tenantId) {
        return CallUtils.filter(Sign::getTenantId, tenantId, this.signs);
    }

    public List<WaybillRelation> getRelations() {
        if (CollectionUtils.isEmpty(this.relations)) {
            return Collections.emptyList();
        }

        var relations = this.relations.stream()
                .map(relation -> new WaybillRelation(this, relation))
                .peek(relation -> {
                    relation.addQuotations(this.getQuotations(relation.getTenantId()));
                    relation.addBills(this.getBills(relation.getTenantId()));
                    relation.addBalances(this.getBalances(relation.getTenantId()));
                    relation.addSigns(this.getSigns(relation.getTenantId()));
                    relation.addReceipts(this.getReceipts(relation.getTenantId()));
                })
                .sorted(Comparator.comparing(Relation::getCharacter))
                .collect(Collectors.toList());
        assert !relations.isEmpty();
        return relations;
    }

    public boolean isCanQuote() {
        // 过关账期不能报价
        if (BooleanUtils.isTrue(this.endBill)) {
            return false;
        }

        return BooleanUtils.isNotTrue(this.getOriginal())
                || CustomerType.MYSELF != this.customer.getType();
    }

    public boolean isCanBill() {
        // 过关账期不能对账
        return BooleanUtils.isNotTrue(this.endBill);
    }
}
